package smtp;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;

import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.Flags;
import javax.mail.Flags.Flag;
import javax.mail.Message;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
/**
 * this Class is responded to parse the received email into display friendly format and canche most errors
 * 
 * 
 * @author liweihan
 *
 */
public class ReceivedEmailParser {


	private MimeMessage mail=null;
	private static StringBuffer textContent = null;
	private LinkedList<BodyPart> attachBody = null;
	private boolean parsed;
	private boolean valid=false;
	private String user;
	public ReceivedEmailParser(String user){
		this.user=user;
	}
	/**
	 * set mimemessage for this class
	 * @param mail
	 */
	public void setMimeMessage(MimeMessage mail){
		this.mail=mail;
		parsed=false;
		textContent = new StringBuffer();
		attachBody = new LinkedList<BodyPart>();
		valid= true;

	}
	/**
	 * get sender anddress
	 * @return
	 */

	public String getFrom(){
		InternetAddress[] address=null;
		String formatted;
		try {
			address = (InternetAddress[]) mail.getFrom();  			
			if(address!=null){
				String from = address[0].getAddress();   
				if (from == null){
					from = "Anonymous address";   				
				}
				String personal = address[0].getPersonal();   
				if (personal == null){
					personal = "";			
				}
				formatted = personal + "<" + from + ">";   			
			}else{
				formatted=user;
			}
		} catch (MessagingException e) {
			formatted = "Can't get FROM";
		}
		return formatted;
	}
	/**
	 * get sender address without name
	 * @return
	 */
	public String getSimpleFrom(){
		InternetAddress[] address=null;
		String formatted;
		try {
			address = (InternetAddress[]) mail.getFrom();  			
			if(address!=null){
				String from = address[0].getAddress();   
				if (from == null){
					from = ""; 				
				}
				formatted = from;
			}else{
				formatted=user;
			}

		} catch (MessagingException e) {
			formatted = "Can't get FROM";
		}
		return formatted;

	}
	/**
	 * get mail address from the server which send the email
	 * @return
	 */
	public String getRealSender(){
		InternetAddress address=null;
		String formatted;
		try {
			address = (InternetAddress) mail.getSender();   
			if (address==null){
				formatted="No agent sender";				
			}else{
				String sender = address.getAddress();   
				if ( sender== null){
					sender = "Anonymous address";   				
				}
				String personal = address.getPersonal();   
				if (personal == null){
					personal = "";  		
				}
				formatted = personal + "<" + sender + ">";   
			}
		} catch (MessagingException e) {
			formatted= "Can't get real SENDER";
		}
		return formatted;

	}
	/**
	 * get sendTo addresses ,could be TO, CC, BCC
	 * 
	 * @param type address type 
	 * @return array of addresses
	 */
	public InternetAddress[] getAddressArray(Message.RecipientType type){
		InternetAddress[] address ;  
		try {
			address = (InternetAddress[]) mail.getRecipients(type);
		} catch (MessagingException e) {
			address=null;
		}
		return address;
	}
	/**
	 * get sendTo addresses ,could be TO, CC, BCC
	 * @param type address type
	 * @return a string of the specified type of address
	 */


	public String getAddress(Message.RecipientType type){
		InternetAddress[] address = null;  
		String all="";
		try {
			address=getAddressArray(type);
			if (address == null) {
				all="No"+type.toString();
			}else{
				for (int i = 0; i < address.length; i++) {   
					String addr = address[i].getAddress();   
					if (addr == null) { 
						addr = ""; 
					} else {   
						addr = MimeUtility.decodeText(addr);
					}   
					String personal = address[i].getPersonal();   
					if (personal == null) {  
						personal = "";   
					} else {   
						personal = MimeUtility.decodeText(personal);   
					}   
					String formatted = personal + "<" + addr + ">";
					if(i!=0){
						all+="; ";
					}
					all +=formatted;   
				}   
			}   
		}catch (UnsupportedEncodingException e) {
			all="Can't decode "+type.toString();
		}
		return all;
	}
	/**
	 * get sendTo addresses with out sender's name, used to display addresses in text field
	 * could be TO, CC, BCC
	 * @param type the type of addresses
	 * @return a string of address, separated by ";"
	 */
	public String getSimpleAddress(Message.RecipientType type){
		InternetAddress[] address = null;  
		String all="";
		try {
			address=getAddressArray(type);
			if (address == null) {
				all="No"+type.toString();
			}else{
				for (int i = 0; i < address.length; i++) {   
					String addr = address[i].getAddress();   
					if (addr == null) { 
						addr = ""; 
					} else {   
						addr = MimeUtility.decodeText(addr);
					}   
					if(i!=0){
						all+=";";
					}
					all +=addr;   
				}   
			}   
		}catch (UnsupportedEncodingException e) {
			all="Can't decode "+type.toString();
		}
		return all;
	}
	/**
	 * get the subject of email
	 * @return subject of email
	 */
	public String getSubject(){
		String subject = "";   
		try {
			subject = mail.getSubject();
			if (subject == null){
				subject = ""; 					
			}
			subject = MimeUtility.decodeText(subject);
		} catch (UnsupportedEncodingException e) {
			subject="Can't decode subject.";
		} catch (MessagingException e) {
			subject="Can't get subject";
		}   
		return subject;   
	}
	/**
	 * get the sent date of current email
	 * @param format the format of returned Date
	 * @return date in specified format
	 */
	public String getSentDate(String format){
		String formatted="";
		if(format==null){
			format = "MM-dd-yyyy HH:mm";			
		}
		Date sentDate=null;
		try {
			sentDate = mail.getSentDate();
			SimpleDateFormat date = new SimpleDateFormat(format);   
			formatted=date.format(sentDate);
		} catch (MessagingException e) {
			formatted="Can't get SentDate";
		}   
		return formatted;   
	}
	/**
	 * get content of this email
	 * if the email is never parsed yet, it will parse Email first
	 * could be a little slow for first parsing 
	 * @return the content of email
	 */
	public String getContent(){
		String content=null;
		if(parsed==true||contentParser(mail)){
			content=textContent.toString();
		}else{
			content="Please type content here.....";
		}
		return content;
	}
	/**
	 * parse content ,save content into textContent and attachBody
	 * @param part will be parsed
	 * @return true if successfully parsed
	 */
	private boolean contentParser(Part part){
		boolean pass=true;
		try {
			boolean isContainTextAttach = part.getContentType().indexOf("name") > 0;
			if (part.isMimeType("text/*") && !isContainTextAttach) {
				textContent.append(mail.getContent().toString());
			} else if (part.isMimeType("message/rfc822")) {
				Message nestedMsg = (Message) part.getContent();
				if (nestedMsg.isMimeType("multipart/*")) {
					parseMultipart( (MimeMultipart) part.getContent());
				}
			} else if (part.isMimeType("multipart/*")) {
				parseMultipart((MimeMultipart) part.getContent());
			}
			parsed=true;
		} catch (IOException e) {
			pass=false;
		} catch (MessagingException e) {
			pass=false;
		}
		return pass;
	}
	/**
	 *private helper method, help to parse content of complicated muti-level Email
	 * @param multipart will be parsed
	 * @throws MessagingException 
	 * @throws IOException
	 */
	private  void parseMultipart(MimeMultipart multipart) throws MessagingException, IOException  {  
		int count;
		count = multipart.getCount();
		for (int i=0;i<count;i++) {  
			BodyPart bodyPart = multipart.getBodyPart(i);  
			if (bodyPart.isMimeType("text/plain")||bodyPart.isMimeType("text/html")) {  
				textContent.append(bodyPart.getContent());
			} else if (BodyPart.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition())) {  
				attachBody.add(bodyPart);
			}else if(bodyPart.isMimeType("multipart/*")) {  
				MimeMultipart att =(MimeMultipart) bodyPart.getContent();  
				parseMultipart(att);
			}

		}
	}
	/**
	 * return a String of the attachments' name, separated by ";" 
	 * @return a string of attachments' name
	 * @throws MessagingException
	 */
	public String getAttachName() throws MessagingException{
		String name="";
		if(parsed==true||contentParser(mail)){
			if(!attachBody.isEmpty()){
				for(int i=0; i< attachBody.size();i++){
					if(i!=0){
						name+=';';
					}
					name += attachBody.get(i).getFileName();
				}
			}else{
				name="No attachment";
			}
		}else{
			name="Can't get fileName";
		}
		return name;

	}
	/**
	 * Save attachment into specified address
	 * 
	 * @param path saving address 
	 * @return true if successfully saved
	 * @throws IOException
	 * @throws MessagingException
	 */
	public boolean saveAttch(String path) throws IOException, MessagingException {
		InputStream is= null;
		OutputStream os= null;
		String fileName=null;
		boolean pass=true;
		try {
			if(attachBody.isEmpty()){
				pass=false;
			}
			for(BodyPart one: attachBody){
				fileName = one.getFileName();  
				is = one.getInputStream();
				os = new FileOutputStream(path+fileName);
				tube(is, os);
			}
		} catch (FileNotFoundException e) {
			throw new FileNotFoundException("path : "+path+fileName+" is invalid");
		}  catch (IOException e) {
			throw new IOException("We got trouble on creating file stream");
		} catch (MessagingException e) {
			throw new MessagingException("We have trouble on accessing content of the emaill message");
		} finally{
			try {
				if (os != null){
					os.close();
				}
				if (is != null){
					is.close();  					
				}
			} catch (IOException e) {
				throw new IOException("Can't close IO stream");
			}					
		}
		return pass;
	}
	/**
	 * help the file stream to pass from the server to user
	 * @param is input stream
	 * @param os output stream
	 * @throws IOException
	 */
	private void tube(InputStream is, OutputStream os) throws IOException {  
		byte[] bytes = new byte[1024];  
		int len = 0;  
		while ((len=is.read(bytes)) != -1 ) {  
			os.write(bytes, 0, len);
		}  
	}  
	/**
	 * get message id of current Email
	 * @return
	 */
	public String getMessageId(){
		String id="";
		try {
			id = mail.getMessageID();
		} catch (MessagingException e) {
			id="Can't get message ID";
		}   
		return id;
	}
	/**
	 * check if a kind of flags is set on current Email
	 * @param type the type of Flag
	 * @return true if set
	 */
	public boolean checkSystemFlag(Flag type){
		boolean found;
		try {
			found=mail.isSet(type);
		} catch (MessagingException e) {
			found=false;
		}
		return found;
	}
	/**
	 * return a string of all the system flags on current Email
	 * help to display on gui
	 * @return String of set flags
	 */
	public String getSystemFlags(){
		String allFlags="";
		try {
			Flags check=mail.getFlags();
			if(check!=null){
				Flag[] all= mail.getFlags().getSystemFlags();
				for(Flag one:all){
					allFlags+=toFlags(one)+" ";
				}
			}
		} catch (MessagingException e) {
			allFlags="Can't get flags, MessagingException";
		}
		return allFlags;
	}
	/**
	 * help to convert from Flag type to correspond sanme
	 * 
	 * @param one a Flag 
	 * @return
	 */
	private String toFlags(Flag one){
		return one.equals(Flag.ANSWERED)?"ANSWERED":
			one.equals(Flag.DELETED)?"DELETED":
				one.equals(Flag.DRAFT)?"DRAFT":
					one.equals(Flag.FLAGGED)?"FLAGGED":
						one.equals(Flag.RECENT)?"RECENT":
							one.equals(Flag.SEEN)?"SEEN":			
								one.equals(Flag.USER)?"USER":"undefined";
	}
	/**
	 * set of cancel a specified type of flag on current email
	 * 
	 * @param type type of flag
	 * @param value true for set flag, false will cancel the flag
	 * @return true if successfully set the flag
	 */

	public boolean setFlage(Flag type,boolean value){
		boolean set;
		try {
			mail.setFlag(type, value);
			mail.saveChanges();
			set=true;
		} catch (MessagingException e) {
			set=false;
		}
		return set;

	}
	/**
	 * get the reply Message, which will include the current of current Email
	 * 
	 * @param toAll
	 * @return
	 */
	public Message getReply(boolean toAll){
		Message reply;
		try {
			reply=mail.reply(toAll);
			reply.setText(getContent());
		} catch (MessagingException e) {
			reply=null;
		}
		return reply;
	}
	/**
	 * get the address to 
	 * @return
	 */
	public InternetAddress[] getReplyTo(){
		InternetAddress[] addr;
		try {
			addr=(InternetAddress[])mail.getReplyTo();
		} catch (MessagingException e) {
			addr=null;
		}
		return addr;
	}
	/**
	 * return the replay to all address
	 * the original replay to all addresses from MSOE is unreliable
	 * so we do it by ourselves
	 * @return a string of addresses, separated by ";"
	 */
	public String getReplayToString(){
		String cc=getSimpleAddress(RecipientType.CC);
		return getSimpleAddress(RecipientType.TO)+(cc.equals("NoCc")?"":";"+cc);
	}
	/**
	 * once the setMimeMessage method is called, valid will be set to true
	 * @return true for ready-to-use
	 */

	public boolean getValid(){
		return valid;
	}



}
